use async_trait::async_trait;
use common::{compose::{CallCore, ContextBox, FError}, utils::gen_ulid};
use crate::{diesel::establish_connection, models::{SpiderTask, SpiderTaskStatus}};
use diesel::prelude::*;

pub fn save_task_status() -> Box<dyn CallCore<(String, String), usize>> {
    struct SaveCallCore {}

    #[async_trait]
    impl CallCore<(String, String), usize> for SaveCallCore {
        async fn run(&self, arg: (String, String), _: ContextBox) -> Result<usize, FError> {
            use crate::schema::spider_task_status::dsl::{spider_task_status, task_data};
            let (task_id, status_json) = arg;
            let connection = &mut establish_connection();
            let rs = diesel::update(spider_task_status.find(task_id))
                .set(task_data.eq(status_json))
                .execute(connection).map_err(|e| FError::Generic(e.to_string()))?;
            Ok(rs)
        }
    }

    Box::new(SaveCallCore {})
}

pub struct SpiderTaskGetter {}
#[async_trait]
impl CallCore<String, SpiderTask> for SpiderTaskGetter {
    async fn run(&self, id: String, _: ContextBox) -> Result<SpiderTask, FError> {
        use crate::schema::spider_task::dsl::spider_task;

        let connection = &mut establish_connection();
        let task = spider_task
            .find(id)
            .select(SpiderTask::as_select())
            .first::<SpiderTask>(connection)
            .map_err(|e| FError::Generic(e.to_string()))?;

        Ok(task)
    } 
}

pub struct SpiderTaskStatusGetter {}
#[async_trait]
impl CallCore<String, SpiderTaskStatus> for SpiderTaskStatusGetter {
    async fn run(&self, id: String, _: ContextBox) -> Result<SpiderTaskStatus, FError> {
        use crate::schema::spider_task_status::dsl::{spider_task_status, task_id};
        
        let connection = &mut establish_connection();
        let task = spider_task_status
            .filter(task_id.eq(id))
            .select(SpiderTaskStatus::as_select())
            .first::<SpiderTaskStatus>(connection)
            .map_err(|e| FError::Generic(e.to_string()))?;
            
        Ok(task)
    } 
}

pub struct SpiderTaskStatusInsertor {}
#[async_trait]
impl CallCore<(String, String), SpiderTaskStatus> for SpiderTaskStatusInsertor {
    async fn run(&self, args: (String, String), _: ContextBox) -> Result<SpiderTaskStatus, FError> {
        use crate::schema::spider_task_status::dsl::*;
        
        let (task_id_val, status_json) = args;
        let connection = &mut establish_connection();
        
        let new_status = SpiderTaskStatus {
            id: gen_ulid(),
            task_id: task_id_val,
            task_data: status_json,
            status: "pending".to_string(),
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
        };
        
        diesel::insert_into(spider_task_status)
            .values(&new_status)
            .execute(connection)
            .map_err(|e| FError::Generic(e.to_string()))?;
            
        Ok(new_status)
    } 
}